/*
 * Copyright (c) 2018, Synopsys, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#define __ASSEMBLY__
#include "include/arc/arc.h"
#include "include/arc/arc_asm_common.h"

.global rt_interrupt_enter;
.global rt_interrupt_leave;
.global context_switch_reqflg;
.global rt_interrupt_from_thread;
.global rt_interrupt_to_thread;
.global exc_nest_count;
.global set_hw_stack_check;

	.text
	.align 4
dispatcher:
	st sp, [r0]
	ld sp, [r1]
#if ARC_FEATURE_STACK_CHECK
#if ARC_FEATURE_SEC_PRESENT
	lr r0, [AUX_SEC_STAT]
	bclr r0, r0, AUX_SEC_STAT_BIT_SSC
	sflag r0
#else
	lr r0, [AUX_STATUS32]
	bclr r0, r0, AUX_STATUS_BIT_SC
	kflag r0
#endif
	jl	set_hw_stack_check
#if ARC_FEATURE_SEC_PRESENT
	lr r0, [AUX_SEC_STAT]
	bset r0, r0, AUX_SEC_STAT_BIT_SSC
	sflag r0
#else
	lr r0, [AUX_STATUS32]
	bset r0, r0, AUX_STATUS_BIT_SC
	kflag r0
#endif
#endif
	pop r0
	j [r0]

/* return routine when task dispatch happened in task context */
dispatch_r:
	RESTORE_NONSCRATCH_REGS
	j	[blink]

/*
 * rt_base_t rt_hw_interrupt_disable();
 */
	.global rt_hw_interrupt_disable
	.align 4
rt_hw_interrupt_disable:
 	clri r0
 	j [blink]


/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
	.global rt_hw_interrupt_enable
	.align 4
rt_hw_interrupt_enable:
	seti r0
	j [blink]


	.global rt_hw_context_switch_interrupt
	.align 4
rt_hw_context_switch_interrupt:
	st r0, [rt_interrupt_from_thread]
	st r1, [rt_interrupt_to_thread]
	mov r0, 1
	st r0, [context_switch_reqflg]
	j [blink]


/*
 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
 * r0 --> from
 * r1 --> to
 */
	.global rt_hw_context_switch
	.align 4
rt_hw_context_switch:
	SAVE_NONSCRATCH_REGS
	mov r2, dispatch_r
	push r2
	b dispatcher


/*
 * void rt_hw_context_switch_to(rt_uint32 to);
 * r0 --> to
 */
	.global rt_hw_context_switch_to
	.align 4
rt_hw_context_switch_to:
	ld sp, [r0]
#if ARC_FEATURE_STACK_CHECK
	mov r1, r0
#if ARC_FEATURE_SEC_PRESENT
	lr r0, [AUX_SEC_STAT]
	bclr r0, r0, AUX_SEC_STAT_BIT_SSC
	sflag r0
#else
	lr r0, [AUX_STATUS32]
	bclr r0, r0, AUX_STATUS_BIT_SC
	kflag r0
#endif
	jl	set_hw_stack_check
#if ARC_FEATURE_SEC_PRESENT
	lr r0, [AUX_SEC_STAT]
	bset r0, r0, AUX_SEC_STAT_BIT_SSC
	sflag r0
#else
	lr r0, [AUX_STATUS32]
	bset r0, r0, AUX_STATUS_BIT_SC
	kflag r0
#endif
#endif
	pop r0
	j [r0]

	.global start_r
	.align 4
start_r:
	pop blink;
	pop r1
	pop r2
	pop r0

	j_s.d [r1]
	kflag r2

/*
 * int __rt_ffs(int value);
 * r0 --> value
 */
	.global __rt_ffs
	.align 4
__rt_ffs:
	breq r0, 0, __rt_ffs_return
	ffs r1, r0
	add r0, r1, 1
__rt_ffs_return:
	j [blink]

/****** exceptions and interrupts handing ******/
/****** entry for exception handling ******/
	.global exc_entry_cpu
	.align 4
exc_entry_cpu:

	EXCEPTION_PROLOGUE

	mov	blink,	sp
	mov	r3, sp		/* as exception handler's para(p_excinfo) */

	ld	r0, [exc_nest_count]
	add	r1, r0, 1
	st	r1, [exc_nest_count]
	brne	r0, 0, exc_handler_1
/* change to exception stack if interrupt happened in task context */
	mov	sp, _e_stack
exc_handler_1:
	PUSH	blink

	lr	r0, [AUX_ECR]
	lsr	r0, r0, 16
	mov	r1, exc_int_handler_table
	ld.as	r2, [r1, r0]

	mov	r0, r3
	jl	[r2]

/* interrupts are not allowed */
ret_exc:
	POP	sp
	mov	r1, exc_nest_count
	ld	r0, [r1]
	sub	r0, r0, 1
	st	r0, [r1]
	brne	r0, 0, ret_exc_1 /* nest exception case */
	lr	r1, [AUX_IRQ_ACT] /* nest interrupt case */
	brne	r1, 0, ret_exc_1

	ld	r0, [context_switch_reqflg]
	brne	r0, 0, ret_exc_2
ret_exc_1:	/* return from non-task context, interrupts or exceptions are nested */
	EXCEPTION_EPILOGUE
	rtie

/* there is a dispatch request */
ret_exc_2:
	/* clear dispatch request */
	mov	r0, 0
	st	r0, [context_switch_reqflg]

	SAVE_CALLEE_REGS	/* save callee save registers */

	/* clear exception bit to do exception exit by SW */
	lr	r0, [AUX_STATUS32]
	bclr	r0, r0, AUX_STATUS_BIT_AE
	kflag	r0

	mov	r1, ret_exc_r	/* save return address */
	PUSH	r1

	ld 	r0, [rt_interrupt_from_thread]
	ld 	r1, [rt_interrupt_to_thread]
	b	dispatcher

ret_exc_r:
	/* recover exception status */
	lr	r0, [AUX_STATUS32]
	bset	r0, r0, AUX_STATUS_BIT_AE
	kflag	r0

	RESTORE_CALLEE_REGS
	EXCEPTION_EPILOGUE
	rtie

/****** entry for normal interrupt exception handling ******/
	.global exc_entry_int	/* entry for interrupt handling */
	.align 4
exc_entry_int:
#if ARC_FEATURE_FIRQ == 1
/*  check whether it is P0 interrupt */
#if ARC_FEATURE_RGF_NUM_BANKS > 1
	lr	r0, [AUX_IRQ_ACT]
	btst	r0, 0
	jnz	exc_entry_firq
#else
	PUSH	r10
	lr	r10, [AUX_IRQ_ACT]
	btst	r10, 0
	POP	r10
	jnz	exc_entry_firq
#endif
#endif
	INTERRUPT_PROLOGUE

	mov	blink, sp

	clri	/* disable interrupt */
	ld	r3, [exc_nest_count]
	add	r2, r3, 1
	st	r2, [exc_nest_count]
	seti	/* enable higher priority interrupt */

	brne	r3, 0, irq_handler_1
/* change to exception stack if interrupt happened in task context */
	mov	sp, _e_stack
#if ARC_FEATURE_STACK_CHECK
#if ARC_FEATURE_SEC_PRESENT
	lr r0, [AUX_SEC_STAT]
	bclr r0, r0, AUX_SEC_STAT_BIT_SSC
	sflag r0
#else
	lr r0, [AUX_STATUS32]
	bclr r0, r0, AUX_STATUS_BIT_SC
	kflag r0
#endif
#endif
irq_handler_1:
	PUSH	blink

	jl 	rt_interrupt_enter

	lr	r0, [AUX_IRQ_CAUSE]
	sr	r0, [AUX_IRQ_SELECT]
	mov	r1, exc_int_handler_table
	ld.as	r2, [r1, r0]	/* r2 = exc_int_handler_table + irqno *4 */
/* handle software triggered interrupt */
	lr	r3, [AUX_IRQ_HINT]
	cmp	r3, r0
	bne.d irq_hint_handled
	xor	r3, r3, r3
	sr	r3, [AUX_IRQ_HINT]
irq_hint_handled:
	lr	r3, [AUX_IRQ_PRIORITY]
	PUSH	r3		/* save irq priority */

	jl	[r2]		/* jump to interrupt handler */
	jl	rt_interrupt_leave
ret_int:
	clri			/* disable interrupt */
	POP	r3		/* irq priority */
	POP	sp
	mov	r1, exc_nest_count
	ld	r0, [r1]
	sub	r0, r0, 1
	st	r0, [r1]
/* if there are multi-bits set in IRQ_ACT, it's still in nest interrupt */
	lr	r0, [AUX_IRQ_CAUSE]
	sr	r0, [AUX_IRQ_SELECT]
	lr 	r3, [AUX_IRQ_PRIORITY]
	lr	r1, [AUX_IRQ_ACT]
	bclr	r2, r1, r3
	brne	r2, 0, ret_int_1

	ld	r0, [context_switch_reqflg]
	brne	r0, 0, ret_int_2
ret_int_1:	/* return from non-task context */
	INTERRUPT_EPILOGUE
	rtie
/* there is a dispatch request */
ret_int_2:
	/* clear dispatch request */
	mov	r0, 0
	st	r0, [context_switch_reqflg]

	/* interrupt return by SW */
	lr	r10, [AUX_IRQ_ACT]
	PUSH	r10
	bclr	r10, r10, r3	/* clear related bits in IRQ_ACT */
	sr	r10, [AUX_IRQ_ACT]

	SAVE_CALLEE_REGS	/* save callee save registers */
	mov	r1, ret_int_r	/* save return address */
	PUSH	r1

	ld 	r0, [rt_interrupt_from_thread]
	ld 	r1, [rt_interrupt_to_thread]
	b	dispatcher

ret_int_r:
	RESTORE_CALLEE_REGS
	/* recover AUX_IRQ_ACT to restore the interrup status */
	POPAX	AUX_IRQ_ACT
	INTERRUPT_EPILOGUE
	rtie

/****** entry for fast irq exception handling ******/
	.global exc_entry_firq
	.weak exc_entry_firq
	.align 4
exc_entry_firq:
	SAVE_FIQ_EXC_REGS

	lr	r0, [AUX_IRQ_CAUSE]
	mov	r1, exc_int_handler_table
/* r2 = _kernel_exc_tbl + irqno *4 */
	ld.as	r2, [r1, r0]

/* for the case of software triggered interrupt */
	lr	r3, [AUX_IRQ_HINT]
	cmp	r3, r0
	bne.d	firq_hint_handled
	xor	r3, r3, r3
	sr	r3, [AUX_IRQ_HINT]
firq_hint_handled:
/* jump to interrupt handler */
	mov	r0, sp
	jl	[r2]

firq_return:
	RESTORE_FIQ_EXC_REGS
	rtie
